#ifndef __COLACONCURRENTE_H_INCLUDED__
#define __COLACONCURRENTE_H_INCLUDED__

#include "Semaforo.h"
#include "SharedMemory.h"
#include <string.h>

#define COLA_MAX_ELEMENTOS 5

/** 
 * Cola concurrente. Soluciona el problema Consumer/producer,
 * compartiendo la cola en en multiples procesos.
 */
template <class T> class ColaConcurrente{
private:
	Semaforo mutex;
	Semaforo lugaresUsados, lugaresDisponibles;
	SharedMemory memoria;	/**< Memoria compartida para el buffer
				 * de datos y variables necesarias
				 * para realizar las operaciones. */

	T* buffer;
	int *final, *frente;

public:
	
	ColaConcurrente();

	void push(const T &dato);
	T pop();

	~ColaConcurrente();
};

template <class T> ColaConcurrente<T>::ColaConcurrente():memoria(COLA_MAX_ELEMENTOS*sizeof(T)+sizeof(*final)+sizeof(*frente)){
	//Reserva memoria compartida de tamaño
	//COLA_MAX_ELEMENTOS*sizeof(T) para el buffer y memoria para
	//final y frente.
	memoria.inicializar();
	void* base = memoria.getDireccion();
	final = (int*)base;
	frente = (int*)((char*)base+sizeof(*final));
	buffer = (T*) ((char*)base + (sizeof(*final)+sizeof(*frente)));
	*final=0;
	*frente=0;
	lugaresDisponibles.setValue(COLA_MAX_ELEMENTOS);
	mutex.setValue(1);
}

template <class T> void ColaConcurrente<T>::push(const T &dato){
	lugaresDisponibles.wait();
	mutex.wait();

	*(buffer + *final) = dato;
	*final = (*final +1) % COLA_MAX_ELEMENTOS;
	mutex.signal();
	lugaresUsados.signal();
}

template <class T> T ColaConcurrente<T>::pop(){
	T dato;
	lugaresUsados.wait();
	mutex.wait();

	dato = *(buffer+*frente);
	*frente = (*frente+1) % COLA_MAX_ELEMENTOS;
	mutex.signal();
	lugaresDisponibles.signal();
	return dato;
}

template <class T> ColaConcurrente<T>::~ColaConcurrente(){
}


#endif //__COLACONCURRENTE_H_INCLUDED__
